package com.qp.basics.estivate.jdbc;

import com.qp.basics.estivate.*;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import javax.transaction.Transaction;
import java.io.Serializable;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Map;

public abstract class AbstractSessionImpl implements Session {
	private static final Log logger = LogFactory
			.getLog(AbstractSessionImpl.class);
	Database database;
	Connection connection;
	int closedStatus;
	String lstSql = null;
	private String name;
	protected SimpleDateFormat dfHHmmssS = new SimpleDateFormat(
			"yyyy-MM-dd HH:mm:ss.S");
	protected SimpleDateFormat dfHHmmss = new SimpleDateFormat(
			"yyyy-MM-dd HH:mm:ss");
	protected SimpleDateFormat dfNoHHmmss = new SimpleDateFormat("yyyy-MM-dd");

	public void close() {
		try {
			connection.close();
			closedStatus = 1;
		} catch (SQLException e) {
			closedStatus = -1;
			throw new RuntimeException(e.getMessage(), e);
		}
	}

	public Connection getConnection() {
		return connection;
	}

	public void setConnection(Connection connection) {
		this.connection = connection;
	}

	public Database getDatabase() {
		return database;
	}

	public void setDatabase(Database database) {
		this.database = database;
	}

	public int delete(Table table, Serializable pk) {
		String pkName = getPkColumn(table).getColumnName();// 不支持复合主键
		return deleteByUnique(table, pkName, pk);
	}

	public Object get(Table table, Serializable pk) {
		Column[] pks = table.getPks();
		if (pks.length != 1) {
			Exception e = new RuntimeException("尚未支持多主键的情况");
			throw new RuntimeException(e.getMessage(), e);
		}
		return findByUnique(table.getTableName(), pks[0].getColumnName(), pk);
	}

	@SuppressWarnings("unchecked")
	public Object merge(Table table, Map entity) {
		Column pkCol = getPkColumn(table);
		String pkName = null;
		Object pk = null;
		if (pkCol != null)
			pkName = pkCol.getColumnName();// 不支持复合主键
		if (pkName != null)
			pk = entity.get(pkName);
		int effectedRows = 0;
		if (pk != null && !"".equals(pk)) {
			effectedRows = update(table, entity);
		}
		if (effectedRows == 0) {
			return insert(table, entity);
		}
		return entity;
	}

	public Object merge(String tableName, Map entity) {
		Table table = database.getTable(tableName);
		return merge(table, entity);
	}

	public Object insert(String tableName, Map entity) {
		Table table = database.getTable(tableName);
		return insert(table, entity);
	}

	public int update(String tableName, Map entity) {
		Table table = database.getTable(tableName);
		String pkName = getPkColumn(table).getColumnName();// 不支持复合主键
		return update(table, entity, pkName);
	}

	public int update(String tableName, Map entity, String pkName) {
		Table table = database.getTable(tableName);
		return update(table, entity, pkName);
	}

	public int update(String tableName, Map entity, String pkName, Object pk) {
		Table table = database.getTable(tableName);
		return update(table, entity, pkName, pk);
	}

	/**
	 * TODO 新插入时数据库支持对主键的返回，但具体做法我还不清楚。
	 *
	 * @param table
	 * @param entity
	 * @return
	 */
	public Object insert(Table table, Map entity) {
		if (logger.isWarnEnabled()) {
			if (!(entity instanceof IgnoreCaseMap)) {
				logger.info("传入的实体不是IgnoreCaseMap对象。");
			}
		}
		String tableName = table.getTableName();
		String sql = "insert into " + tableNameWrap(tableName) + " ";
		Query q = this.createQuery(null);
		String colNameStr = "";
		String qMarkStr = "";
		int cnt = 1;
		Column[] cols = table.getColumns();
		StringBuffer sb = null;
		if (logger.isInfoEnabled()) {
			sb = new StringBuffer();
		}
		for (Column column : cols) {
			String columnName = column.getColumnName();
			if (entity.containsKey(columnName)) {
				if (cnt != 1) {
					colNameStr += ',';
					qMarkStr += ',';
				}
				colNameStr += columnNameWrap(columnName);
				qMarkStr += '?';
				q.setParameter(cnt, entity.get(columnName));
				cnt++;
			} else {
				if (logger.isInfoEnabled()) {
					sb.append(columnName + " ");
				}
			}
		}
		if (logger.isInfoEnabled()) {
			logger.info("[" + sb + "]没有包含在Map当中。");
		}
		sql += "(" + colNameStr + ") values (" + qMarkStr + ")";
		if (logger.isDebugEnabled())
			logger.debug("sql=[" + sql + "]");
		q.setSql(sql);
		q.executeUpdate();
		Object key = q.getGeneratedKey();
		if (key != null && table.getPks() != null && table.getPks().length == 1) {
			entity.put(table.getPks()[0].getColumnName(), key);
		}
		return entity;
	}

	/**
	 * 更新。
	 *
	 * @param table
	 * @param entity
	 * @return
	 */
	public int update(Table table, Map entity) {
		// String pkName = getPkColumn(table).getColumnName();//不支持复合主键
		Column[] pkArr = table.getPks();
		if (pkArr.length > 0) {
			return update(table, entity, pkArr, null);
		} else {
			throw new RuntimeException("该表" + table.getTableName()
					+ "没有主键表，请指定需要更新的字段。");
		}
	}

	public int update(Table table, Map entity, String pkName) {
		return update(table, entity, pkName, null);
	}

	/**
	 * 如果字段的数据类型是时间，而map里的内容是日期格式的字符串，咋办，就是本方法要做的了。
	 * 不同的数据库可能还有自己特定的处理需求，那就重写本方法了。
	 *
	 * @param v
	 * @param dataType
	 * @return
	 */
	protected Object valueConvert(Object v, String dataType) {
		if (v == null)
			return v;
		if (dataType == null)
			return v;
		Object rtn = v;
		if (dataType != null
				&& (dataType.contains("date") || dataType.contains("time"))) {
			if (!(v instanceof Date)) {
				String dateStr = v.toString();
				try {
					if (dateStr.indexOf('.') > 0)
						rtn = dfHHmmssS.parse(dateStr);
					if (dateStr.indexOf(':') > 0)
						rtn = dfHHmmss.parse(dateStr);
					if (dateStr.indexOf('-') > 0)
						rtn = dfNoHHmmss.parse(dateStr);
				} catch (ParseException e) {
					throw new RuntimeException(e.getMessage(), e);
				}
			}
		}
		return rtn;
	}

	public int update(Table table, Map entity, String pkName, Object pk) {
		String tableName = table.getTableName();
		String sql = "update " + tableNameWrap(tableName) + " ";
		Query q = this.createQuery(null);
		int cnt = 1;
		Column[] cols = table.getColumns();
		for (Column column : cols) {
			String columnName = column.getColumnName();
			String dataType = column.getDataType();
			if (entity.containsKey(columnName)) {
				if (cnt == 1) {
					sql += " set ";
				} else {
					sql += ',';
				}
				sql += " " + columnNameWrap(columnName) + "=?";
				Object v = entity.get(columnName);
				v = valueConvert(v, dataType);
				q.setParameter(cnt, v);
				cnt++;
			} else {
				if (logger.isInfoEnabled()) {
					logger.info("[" + columnName + "]没有包含在Map当中。");
				}
			}
		}
		sql += " where " + columnNameWrap(pkName) + "=?";
		if (pk == null)
			pk = entity.get(pkName);
		q.setParameter(cnt, pk);

		if (logger.isDebugEnabled())
			logger.debug("sql=[" + sql + "]");
		q.setSql(sql);
		return q.executeUpdate();

	}

	public int update(Table table, Map entity, Column[] compoundKeyArr,
			Object[] keyValueArr) {
		String[] compoundKeyArr2 = new String[compoundKeyArr.length];
		for (int i = 0; i < compoundKeyArr.length; i++) {
			compoundKeyArr2[i] = compoundKeyArr[i].getColumnName();
		}
		return update(table, entity, compoundKeyArr2, keyValueArr);
	}

	public int update(String tableName, Map entity, String[] compoundKeyArr,
			Object[] keyValueArr) {
		Table table = database.getTable(tableName);
		return update(table, entity, compoundKeyArr, keyValueArr);
	}

	/**
	 * 按照复合主键更新，也可以按照指定字段更新!!
	 *
	 * @param table
	 * @param entity
	 *            如果指定字段，entity中不能有主键字段
	 * @param compoundKeyArr
	 *            忽略大小写
	 * @param keyValueArr
	 *            要么为null,要么数组大小和compoundKeyArr一致
	 * @return
	 */
	public int update(Table table, Map entity, String[] compoundKeyArr,
			Object[] keyValueArr) {
		String tableName = table.getTableName();
		String sql = "update " + tableNameWrap(tableName) + " ";
		Query q = this.createQuery(null);
		int cnt = 1;
		Column[] cols = table.getColumns();
		for (Column column : cols) {
			String columnName = column.getColumnName();
			String dataType = column.getDataType();
			if (entity.containsKey(columnName)) {
				if (cnt == 1) {
					sql += " set ";
				} else {
					sql += ',';
				}
				sql += " " + columnNameWrap(columnName) + "=?";
				Object v = entity.get(columnName);
				v = valueConvert(v, dataType);
				q.setParameter(cnt, v);
				cnt++;
			} else {
				if (logger.isInfoEnabled()) {
					logger.info("[" + columnName + "]没有包含在Map当中。");
				}
			}
		}
		// 复合主键的数组循环
		if (keyValueArr != null && keyValueArr.length != 0
				&& keyValueArr.length != compoundKeyArr.length) {
			throw new RuntimeException("给出的复合主键和复合主键值数量不一致!!复合主键=["
					+ compoundKeyArr + "],复合主键值=[" + keyValueArr + "]");
		}
		if (compoundKeyArr.length > 0) {
			for (int i = 0; i < compoundKeyArr.length; i++) {
				boolean ifExistTheColumn = false;
				for (Column column : cols) {
					String columnName = column.getColumnName();
					if (columnName.toUpperCase().equals(
							compoundKeyArr[i].toUpperCase())) {
						if (
								keyValueArr == null
								&& !entity.containsKey(columnName.toUpperCase())
								&& !entity.containsKey(columnName.toLowerCase())
						) {
							throw new RuntimeException("数据Map中没有该复合主键:"
									+ compoundKeyArr[i].toUpperCase()
									+ "这个key!!");
						} else {
							ifExistTheColumn = true;
						}
					}
				}
				if (!ifExistTheColumn) {
					throw new RuntimeException("表" + table.getTableName()
							+ "中没有该复合主键:" + compoundKeyArr[i] + "这个字段!!");
				}
				if (i == 0)
					sql += " where ";
				else {
					sql += " and ";
				}
				sql += compoundKeyArr[i] + "=? ";
				if (keyValueArr == null || keyValueArr.length == 0) {
					q.setParameter(cnt++, entity.get(compoundKeyArr[i]));
				} else {
					q.setParameter(cnt++, keyValueArr[i]);
				}
			}
		} else {
			throw new RuntimeException("复合主键为空!!");
		}

		if (logger.isDebugEnabled())
			logger.debug("sql=[" + sql + "]");
		q.setSql(sql);
		return q.executeUpdate();
	}

	public int delete(String tableName, Serializable pk) {
		Table table = database.getTable(tableName);
		String pkName = getPkColumn(table).getColumnName();// 不支持复合主键
		return deleteByUnique(table, pkName, pk);
	}

	public int delete(String tableName, Object entity) {
		Table table = database.getTable(tableName);
		return delete(table, entity);
	}

	public int delete(Table table, Object entity) {
		if (!(entity instanceof Map)) {
			RuntimeException e = new RuntimeException("尚不支持Map之外的实体类型。");
			throw e;
		}
		Map m = (Map) entity;
		String pkName = getPkColumn(table).getColumnName();// 不支持复合主键
		return deleteByUnique(table, pkName, m.get(pkName));
	}

	public Object findByUnique(String tableName, String colName, Object v) {
		String sql = "select * from " + tableNameWrap(tableName) + " where "
				+ columnNameWrap(colName) + "=?";
		Query q = this.createQuery(sql);
		q.setParameter(1, v);
		return q.getSingleResult();
	}

	public Object findByUnique(Table table, String colName, Object v) {
		return findByUnique(table.getTableName(), colName, v);
	}

	public Object get(String tableName, Serializable pk) {
		Table table = database.getTable(tableName);
		String pkName = getPkColumn(table).getColumnName();// 不支持复合主键
		return findByUnique(tableName, pkName, pk);
	}

	public int deleteByUnique(String tableName, String colName, Object v) {

		String sql = "delete from " + tableNameWrap(tableName) + " where "
				+ columnNameWrap(colName) + "=?";
		Query q = this.createQuery(sql);
		q.setParameter(1, v);
		return q.executeUpdate();
	}

	public int deleteByUnique(Table table, String colName, Object v) {
		return deleteByUnique(table.getTableName(), colName, v);
	}

	public Transaction beginTransaction() {
		throw new RuntimeException("beginTransaction 尚未实现");
	}

	public Transaction getTransaction() {
		throw new RuntimeException("getTransaction  尚未实现");
	}

	private Column getPkColumn(Table table) {
		Column[] pks = table.getPks();
		if (pks.length != 1) {
			// Exception e = new
			// NotSupportedException("尚未支持复合主键的情况 tableName=["+database.getTableSchema()+"."+table.getTableName()+"]");
			// throw new RuntimeException(e.getMessage(),e);
			if (logger.isWarnEnabled()) {
				logger.warn("尚未支持复合主键的情况 tableName=["
						+ database.getTableSchema() + "."
						+ table.getTableName() + "]，本表主键数量为" + pks.length);
			}
		}
		return pks[0];
	}

	protected String columnNameWrap(String colName) {
		return colName;
	}

	protected String tableNameWrap(String tableName) {
		return tableName;
	}

	public int[] executeBatch(String[] sqls) {
		if (logger.isDebugEnabled()) {
			String s = Arrays.toString(sqls);
			logger.debug(s);
		}
		Statement sm = null;
		try {
			sm = this.connection.createStatement();
			for (String sql : sqls) {
				if (!sql.toString().trim().equals("")) {
					sm.addBatch(sql);
					lstSql = sql;
				}
			}
			return sm.executeBatch();
		} catch (SQLException e) {
			String s = Arrays.toString(sqls);
			throw new RuntimeException(e.getMessage() + " sqls=" + s, e);
		} finally {
			if (sm != null)
				try {
					sm.close();
				} catch (SQLException e) {
					logger.error(e.getMessage(), e);
				}
		}
	}

	public int executeUpdate(String sql, Object... params) {
		Query q = createQuery(sql);
		if (params != null)
			for (int i = 0; i < params.length; i++) {
				q.setParameter(i + 1, params[i]);
			}
		return q.executeUpdate();
	}

	public Object queryForObject(String sql, Object... params) {
		Query q = createQuery(sql);
		if (params != null)
			for (int i = 0; i < params.length; i++) {
				q.setParameter(i + 1, params[i]);
			}
		return q.getSingleColumnResult();
	}

	public String queryForString(String sql, Object... params) {
		Query q = createQuery(sql);
		if (params != null)
			for (int i = 0; i < params.length; i++) {
				q.setParameter(i + 1, params[i]);
			}
		return (String) q.getSingleColumnResult();
	}

	public Number queryForNumber(String sql, Object... params) {
		Query q = createQuery(sql);
		if (params != null)
			for (int i = 0; i < params.length; i++) {
				q.setParameter(i + 1, params[i]);
			}
		Number num = (Number) q.getSingleColumnResult();
		return num;
	}

	public Long queryForLong(String sql, Object... params) {
		Number num = queryForNumber(sql, params);
		if (num == null)
			return null;
		return num.longValue();
	}

	public Integer queryForInt(String sql, Object... params) {
		Number num = queryForNumber(sql, params);
		if (num == null)
			return null;
		return num.intValue();
	}

	public Map queryForMap(String sql, Object... params) {
		Query q = createQuery(sql);
		if (params != null)
			for (int i = 0; i < params.length; i++) {
				q.setParameter(i + 1, params[i]);
			}
		Map m = (Map) q.getSingleResult();
		return m;
	}

	public List<Map> queryForList(String sql, Object... params) {
		Query q = createQuery(sql);
		if (params != null)
			for (int i = 0; i < params.length; i++) {
				q.setParameter(i + 1, params[i]);
			}
		List<Map> l = q.getResultList();
		return l;
	}

	public int getClosedStatus() {
		return closedStatus;
	}

	public String getLstSql() {
		return lstSql;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
}
